﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BUS;
using DTO;
using System.IO;
using System.Xml;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Xml2Sprite
{
    class Program
    {
        static void Main(string[] args)
        {
            //StreamReader re = File.OpenText(args[0]);
            SpriteDTO sprite;
            //sprite = Load("cay3.sml");
            //SpriteBUS.Save(sprite, "eater_plant_3.xml");

            //sprite = Load("cay1.sml");
            //SpriteBUS.Save(sprite, "eater_plant_1.xml");

            //sprite = Load("cay2.sml");
            //SpriteBUS.Save(sprite, "eater_plant_2.xml");

            //args[0] = "eater_plant_2.sml";
            bool bPad = false;
            if (args.Length > 1 && args[1] == "true")
                bPad = true;
            sprite = Load(args[0], bPad);
            SpriteBUS.Save(sprite, Path.ChangeExtension(args[0], "xml"));

            //sprite = Load("cay_dua.sml");
            //SpriteBUS.Save(sprite, "cay_dua.xml");

        }

        static float[] ParseFloatList(string strList)
        {
            string[] strs = strList.Split(' ');
            float[] floats = new float[strs.Length];
            for (int i = 0; i < strs.Length; i++)
            {
                floats[i] = float.Parse(strs[i]);
            }

            return floats;
        }

        static int[] ParseIntList(string strList)
        {
            string[] strs = strList.Split(' ');
            int[] ints = new int[strs.Length];
            for (int i = 0; i < strs.Length; i++)
            {
                ints[i] = int.Parse(strs[i]);
            }

            return ints;
        }

        static Module ReadModule(XmlReader reader, int width, int height)
        {
            Module module = new Module();
            bool bEnd = false;

            float[] x = new float[16];
            float[] y = new float[16];
            float[] u = new float[16];
            float[] v = new float[16];

            int iVer = 0;
            int iTex = 0;

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name == "Vertex")
                        {
                            x[iVer] = float.Parse(reader.GetAttribute("x"));
                            y[iVer] = float.Parse(reader.GetAttribute("y"));
                            iVer++;
                        }
                        else if (reader.Name == "TexCoord")
                        {
                            u[iTex] = float.Parse(reader.GetAttribute("u"));
                            v[iTex] = float.Parse(reader.GetAttribute("v"));
                            iTex++;
                        }
                        else if (reader.Name == "FillStyle")
                        {
                            module.Bmp_Id = int.Parse(reader.GetAttribute("bmp_id"));
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (reader.Name == "Shape")
                            bEnd = true;
                        break;
                }

                if (bEnd)
                    break;
            }

            float maxX = 0;
            float maxY = 0;
            float minU = 1;
            float minV = 1;
            for (int i = 0; i < iVer; i++)
            {
                if (x[i] > maxX)
                    maxX = x[i];
                if (y[i] > maxY)
                    maxY = y[i];
                if (u[i] < minU)
                    minU = u[i];
                if (v[i] < minV)
                    minV = v[i];
            }
            module.W = (int)(maxX + 0.5f);
            module.H = (int)(maxY + 0.5f);
            module.X = (int)(minU * width + 0.5f);
            module.Y = (int)(minV * height + 0.5f);

            if (maxX == x[2] && maxY == y[2])
                module.TmpFlag = SpriteDTO.FLIP_X;
            return module;
        }

        static List<FModule> ReadFModule(XmlReader reader)
        {
            List<FModule> fmodules = new List<FModule>();

            bool bEnd = false;

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name == "DispObj")
                        {
                            FModule fmodule = new FModule();

                            fmodule.Mid = int.Parse(reader.GetAttribute("shp_id"));

                            string mat = reader.GetAttribute("matrix");
                            if (mat != null)
                            {
                                float[] floats = ParseFloatList(reader.GetAttribute("matrix"));
                                fmodule.ScaleX = Math.Abs(floats[0]);
                                fmodule.ScaleY = Math.Abs(floats[1]);
                                fmodule.Ox = (int)(floats[4] + 0.5f);
                                fmodule.Oy = (int)(floats[5] + 0.5f);

                                fmodule.Flag = 0;
                                if (floats[0] == -1.0f)
                                {
                                    fmodule.Flag |= SpriteDTO.FLIP_X;
                                }
                                if (floats[1] == -1.0f)
                                {
                                    fmodule.Flag |= SpriteDTO.FLIP_Y;
                                }
                            }
                            else
                            {
                                fmodule.ScaleX = 1;
                                fmodule.ScaleY = 1;
                                fmodule.Ox = 0;
                                fmodule.Oy = 0;
                                fmodule.Flag = 0;
                            }

                            string cxform = reader.GetAttribute("cxform");
                            if (cxform != null)
                            {
                                float[] floats = ParseFloatList(reader.GetAttribute("cxform"));

                                fmodule.Alpha = (byte)(floats[3] * 255);
                            }
                            else
                            {
                                fmodule.Alpha = 255;
                            }
                            fmodules.Add(fmodule);
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (reader.Name == "DispObjs")
                            bEnd = true;
                        break;
                }

                if (bEnd)
                    break;
            }

            return fmodules;
        }

        static SpriteDTO Load(string filename, bool bPad)
        {
            SpriteDTO sprite = new SpriteDTO();
            sprite.FilePath = filename;

            XmlReader reader = XmlReader.Create(File.Open(filename, FileMode.Open, FileAccess.Read));

            if (reader == null)
                return null;

            int width = -1;
            int height = -1;

            List<FModule> fmodules = new List<FModule>();
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name == "Bitmap")
                        {
                            ImageInfo imageInfo = new ImageInfo();
                            imageInfo.Path = reader.GetAttribute("name");
                            width = int.Parse(reader.GetAttribute("width"));
                            height = int.Parse(reader.GetAttribute("height"));

                            sprite.ImageInfo.Add(imageInfo);
                        }
                        else if (reader.Name == "Shape")
                        {
                            Module module = ReadModule(reader, width, height);
                            module.Id = sprite.Modules.Count;
                            sprite.Modules.Add(module);
                        }
                        else if (reader.Name == "DispObjs")
                        {
                            fmodules = ReadFModule(reader);

                            for (int i = 0; i < fmodules.Count; i++)
                            {
                                FModule fmodule = fmodules[i];
                                if ((fmodule.Flag & SpriteDTO.FLIP_X) != 0)
                                {
                                    fmodule.Ox -= sprite.Modules[fmodule.Mid].W;
                                }
                                if ((fmodule.Flag & SpriteDTO.FLIP_Y) != 0)
                                {
                                    fmodule.Oy -= sprite.Modules[fmodule.Mid].H;
                                }

                                //if (sprite.Modules[fmodule.Mid].TmpFlag != 0)
                                //{
                                //    fmodule.Flag ^= sprite.Modules[fmodule.Mid].TmpFlag;
                                //}
                            }
                        }
                        else if (reader.Name == "Frame")
                        {
                            Frame frame = new Frame();
                            frame.Id = sprite.Frames.Count;

                            string dispobjs = reader.GetAttribute("dispobjs");
                            if (dispobjs != null)
                            {
                                int[] ints = ParseIntList(dispobjs);
                                for (int i = 0; i < ints.Length; i++)
                                {
                                    frame.FModules.Add(fmodules[ints[i]]);
                                }
                            }

                            sprite.Frames.Add(frame);
                        }
                        else if (reader.Name == "Animation")
                        {
                            Animation animation = new Animation();
                            animation.Id = sprite.Animations.Count;
                            animation.Desc = reader.GetAttribute("name");

                            int[] ints = ParseIntList(reader.GetAttribute("frames"));
                            for (int i = 0; i < ints.Length; i++)
                            {
                                if (animation.AFrames.Count > 0 && animation.AFrames[animation.AFrames.Count - 1].Fid == ints[i])
                                {
                                    animation.AFrames[animation.AFrames.Count - 1].Time++;
                                }
                                else
                                {
                                    AFrame aframe = new AFrame();
                                    aframe.Fid = ints[i];
                                    aframe.Time = 1;

                                    animation.AFrames.Add(aframe);
                                    if (sprite.Frames[aframe.Fid].Desc == "")
                                    {
                                        if (animation.AFrames.Count == 1)
                                            sprite.Frames[aframe.Fid].Desc = animation.Desc;
                                        else if (animation.AFrames.Count == 2)
                                        {
                                            sprite.Frames[animation.AFrames[animation.AFrames.Count - 2].Fid].Desc = animation.Desc + "_" + (animation.AFrames.Count - 1).ToString();
                                            sprite.Frames[aframe.Fid].Desc = animation.Desc + "_" + animation.AFrames.Count.ToString();
                                        }
                                        else
                                            sprite.Frames[aframe.Fid].Desc = animation.Desc + "_" + animation.AFrames.Count.ToString();
                                    }
                                }
                            }

                            sprite.Animations.Add(animation);
                        }
                        break;

                    case XmlNodeType.EndElement:

                        break;
                }
            }

            //if (sprite.Animations.Count > 0 && sprite.Animations[sprite.Animations.Count - 1].Desc == "map_module")
            {
                MapModule(sprite, bPad);
            }

            return sprite;
        }

        private static void MapModule(SpriteDTO sprite, bool bPad)
        {
            int fid = sprite.Animations[sprite.Animations.Count - 1].AFrames[0].Fid;
            Frame frame = sprite.Frames[fid];

            for (int i = 0; i < sprite.ImageInfo.Count; i++)            
                sprite.ImageInfo[i].Image = (Bitmap)Image.FromFile(sprite.ImageInfo[i].Path);
            sprite.ImageInfo[0].Path = Path.GetFileNameWithoutExtension(sprite.FilePath) + "_pck.png";

            if (sprite.Animations.Count > 0 && sprite.Animations[sprite.Animations.Count - 1].Desc == "map_module")
            {
                sprite.Animations.RemoveAt(sprite.Animations.Count - 1);
                sprite.Frames.Remove(frame);
            }

            RectanglePacker.Pack(sprite, bPad);
            return;
            //for (int i = 0; i < sprite.ImageInfo.Count; i++)
            //    sprite.ImageInfo[i].Image = (Bitmap)Image.FromFile(sprite.ImageInfo[i].Path);
            
            //int width = sprite.GetFrameWidth(frame);
            //int height = sprite.GetFrameHeight(frame);

            //if (width < height)
            //    width = height;
            //int k = 1;
            //int ex_width = 1;
            //while (ex_width < width)
            //{
            //    ex_width *= 2;
            //}
            //Bitmap buffer = new Bitmap(ex_width, ex_width, PixelFormat.Format32bppArgb);

            //Graphics g = Graphics.FromImage(buffer);
            //g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            //g.PageUnit = GraphicsUnit.Pixel;
            ////g.Clear(Color.FromArgb(255,255,255,255));
            //sprite.PaintFrame(g, frame, 0, 0, 0);
            
            //g.Dispose();
            ////g.ReleaseHdc();
            string newImageName = Path.ChangeExtension(sprite.FilePath, "png");
            //buffer.Save(newImageName, ImageFormat.Png);

            for (int i = 0; i < frame.FModules.Count; i++)
            {
                Module module = sprite.Modules[frame.FModules[i].Mid];
                module.X = frame.FModules[i].Ox;
                module.Y = frame.FModules[i].Oy;
            }

            sprite.Animations.RemoveAt(sprite.Animations.Count - 1);
            sprite.Frames.Remove(frame);

            sprite.ImageInfo.Clear();
            ImageInfo newImageInfo = new ImageInfo();
            newImageInfo.Path = newImageName;
            sprite.ImageInfo.Add(newImageInfo);
        }
    }

}
